use crate::protocol::{RedisCommand, RespValue};
use crate::storage::{Storage, StorageEngine, RedisValue};

/// GET key
pub async fn get(storage: &Storage, command: RedisCommand) -> RespValue {
    if command.arg_count() != 1 {
        return RespValue::error("ERR wrong number of arguments for 'get' command");
    }

    let key = &command.args[0];

    match storage.get(key) {
        Some(RedisValue::String(value)) => RespValue::bulk_string(value),
        Some(_) => RespValue::error("WRONGTYPE Operation against a key holding the wrong kind of value"),
        None => RespValue::null_bulk_string(),
    }
}

/// SET key value [EX seconds]
pub async fn set(storage: &Storage, command: RedisCommand) -> RespValue {
    if command.arg_count() < 2 {
        return RespValue::error("ERR wrong number of arguments for 'set' command");
    }

    let key = command.args[0].clone();
    let value = command.args[1].clone();

    let mut ttl = None;

    // Parse optional EX parameter
    if command.arg_count() >= 4 && command.args[2].to_uppercase() == "EX" {
        match command.args[3].parse::<u64>() {
            Ok(seconds) => ttl = Some(seconds),
            Err(_) => return RespValue::error("ERR invalid expire time in 'set' command"),
        }
    }

    storage.set(key, RedisValue::String(value), ttl);
    RespValue::ok()
}

/// APPEND key value
pub async fn append(storage: &Storage, command: RedisCommand) -> RespValue {
    if command.arg_count() != 2 {
        return RespValue::error("ERR wrong number of arguments for 'append' command");
    }

    let key = &command.args[0];
    let append_value = &command.args[1];

    match storage.get(key) {
        Some(RedisValue::String(mut existing_value)) => {
            existing_value.push_str(append_value);
            let new_len = existing_value.len();
            storage.set(key.clone(), RedisValue::String(existing_value), None);
            RespValue::integer(new_len as i64)
        }
        Some(_) => RespValue::error("WRONGTYPE Operation against a key holding the wrong kind of value"),
        None => {
            storage.set(key.clone(), RedisValue::String(append_value.clone()), None);
            RespValue::integer(append_value.len() as i64)
        }
    }
}

/// INCR key
pub async fn incr(storage: &Storage, command: RedisCommand) -> RespValue {
    if command.arg_count() != 1 {
        return RespValue::error("ERR wrong number of arguments for 'incr' command");
    }

    let key = &command.args[0];

    match storage.get(key) {
        Some(RedisValue::String(value)) => {
            match value.parse::<i64>() {
                Ok(num) => {
                    let new_value = num + 1;
                    storage.set(key.clone(), RedisValue::String(new_value.to_string()), None);
                    RespValue::integer(new_value)
                }
                Err(_) => RespValue::error("ERR value is not an integer or out of range"),
            }
        }
        Some(_) => RespValue::error("WRONGTYPE Operation against a key holding the wrong kind of value"),
        None => {
            storage.set(key.clone(), RedisValue::String("1".to_string()), None);
            RespValue::integer(1)
        }
    }
}

/// DECR key
pub async fn decr(storage: &Storage, command: RedisCommand) -> RespValue {
    if command.arg_count() != 1 {
        return RespValue::error("ERR wrong number of arguments for 'decr' command");
    }

    let key = &command.args[0];

    match storage.get(key) {
        Some(RedisValue::String(value)) => {
            match value.parse::<i64>() {
                Ok(num) => {
                    let new_value = num - 1;
                    storage.set(key.clone(), RedisValue::String(new_value.to_string()), None);
                    RespValue::integer(new_value)
                }
                Err(_) => RespValue::error("ERR value is not an integer or out of range"),
            }
        }
        Some(_) => RespValue::error("WRONGTYPE Operation against a key holding the wrong kind of value"),
        None => {
            storage.set(key.clone(), RedisValue::String("-1".to_string()), None);
            RespValue::integer(-1)
        }
    }
}
